// Author: Domien Nowicki

#include <assert.h>
#include <QPainter>
#include <QGridLayout>
#include <math.h>
#include "RaycasterWidget.h"




RaycasterWidget::RaycasterWidget(RaycasterWidget::Renderer rendererType, QWidget *parent, Qt::WFlags flags): QWidget(parent, flags), renderer(0), mousePressed(false)
{
	setFocusPolicy(Qt::StrongFocus);

	// Initialize keyboard stuff
	InitKeys();

	// Create the main layout
    mainLayout = new QGridLayout;
	mainLayout->setMargin(0);
	mainLayout->setSpacing(0);

	// Plug in the chosen renderer
	SetRenderer(rendererType);
    setLayout(mainLayout);

	// Start the update timer
	startTimer(50);
}

RaycasterWidget::~RaycasterWidget()
{
	if (0 != renderer)
	{
		delete renderer;
	}
}

void RaycasterWidget::SetRenderer(RaycasterWidget::Renderer rendererType)
{
	if (0 != renderer)
	{
		delete renderer;
		renderer = 0;
	}

	if (SOFTWARE == rendererType)
	{
		renderer = new SoftRendererWidget(this);
	}
	else if (OPENGL == rendererType)
	{
		renderer = new GLRendererWidget(this);
	}

    mainLayout->addWidget(renderer, 0,0);
}

void RaycasterWidget::timerEvent(QTimerEvent *e)
{
	if (KeyPressed(KEY_LEFT))
	{
		TurnLeft();
	}

	if (KeyPressed(KEY_RIGHT))
	{
		TurnRight();
	}

	if (KeyPressed(KEY_UP))
	{
		MoveForward();
	}

	if (KeyPressed(KEY_DOWN))
	{
		MoveBackward();
	}

	if (mousePressed)
	{
		if (ratioTurn < 0)
		{
			TurnLeft(-ratioTurn);
		}
		else
		{
			TurnRight(ratioTurn);
		}

		if (ratioMove < 0)
		{
			MoveBackward(-ratioMove);
		}
		else
		{
			MoveForward(ratioMove);
		}
	}

	Render();
	Update();
}

void RaycasterWidget::Update()
{
	assert(0 != renderer);

	renderer->Update();
}


void RaycasterWidget::SetPixel(unsigned int x, unsigned int y, const Pixel& pixel)
{
	assert(0 != renderer);

	renderer->SetPixel(x, y, pixel); 
}

void RaycasterWidget::DrawPolygon(const Poly& poly)
{
	assert(0 != renderer);

	renderer->DrawPolygon(poly);
}

Pixel RaycasterWidget::GetPixel(unsigned int x, unsigned int y) const
{
	assert(0 != renderer);

	return renderer->GetPixel(x, y);
}

unsigned int RaycasterWidget::GetHeight() const
{
	assert(0 != renderer);

	return renderer->GetHeight();
}

unsigned int RaycasterWidget::GetWidth() const
{
	assert(0 != renderer);

	return renderer->GetWidth();
}

void RaycasterWidget::resizeEvent(QResizeEvent *e)
{
}

QSize RaycasterWidget::sizeHint() const
{
	return QSize(100,100);
}

QSize RaycasterWidget::minimumSizeHint() const
{
	return QSize(100, 100);
}

QSize RaycasterWidget::maximumSizeHint() const
{
	return QSize(400, 200);
}

void RaycasterWidget::paintEvent(QPaintEvent *e)
{
}

void RaycasterWidget::InitKeys()
{
	grabKeyboard();

	for(int i=0; i<KEY_MAX; i++)
		keys[i] = false;
}

bool RaycasterWidget::KeyPressed(enum RaycasterWidget::Keys key)
{
	if (key == KEY_MAX)
		return false;

	return keys[key];
}


void RaycasterWidget::keyReleaseEvent(QKeyEvent *e)
{
	switch (e->key()) 
	{
		case Qt::Key_Up:
			keys[KEY_UP] = false;
		break;

		case Qt::Key_Down:
			keys[KEY_DOWN] = false;
		break;

		case Qt::Key_Left:
			keys[KEY_LEFT] = false;
		break;
		
		case Qt::Key_Right:
			keys[KEY_RIGHT] = false;
		break;

		default:
		QWidget::keyPressEvent(e);
	}
}

void RaycasterWidget::keyPressEvent(QKeyEvent *e)
{
	switch (e->key()) 
	{
		case Qt::Key_Up:
			keys[KEY_UP] = true;
		break;

		case Qt::Key_Down:
			keys[KEY_DOWN] = true;
		break;

		case Qt::Key_Left:
			keys[KEY_LEFT] = true;
		break;
		
		case Qt::Key_Right:
			keys[KEY_RIGHT] = true;
		break;

		default:
		QWidget::keyPressEvent(e);
	}
}

void RaycasterWidget::mousePressEvent(QMouseEvent *e)
{
	mousePressed = true;
	lastMouseX = e->pos().x();
	lastMouseY = e->pos().y();

	CalculateMouseMovement(lastMouseX, lastMouseY);
}

void RaycasterWidget::mouseReleaseEvent(QMouseEvent *e)
{
	mousePressed = false;
}

void RaycasterWidget::mouseMoveEvent(QMouseEvent *e)
{
	lastMouseX = e->pos().x();
	lastMouseY = e->pos().y();

	CalculateMouseMovement(lastMouseX, lastMouseY);
}

void RaycasterWidget::CalculateMouseMovement(double mouseX, double mouseY)
{
	// Offset to center of widget
	double midX = width()/2;
	double midY = height()/2;

	// Calculate mouse pointer coordinates from center of the widget
	mouseX = mouseX - midX;
	mouseY = mouseY - midY;

	// Calculate turn and move ratio's (basically, the border of the widget equals 1, the center equals 0)
	ratioTurn = mouseX*2/width();
	ratioMove = -mouseY*2/height();
}


